#pragma once

#include "Transition.h"

#include "Behavior.h"
#include "Classifier.h"
#include "Constraint.h"
#include "RedefinableElement.h"
#include "Region.h"
#include "StateMachine.h"
#include "Trigger.h"
#include "Vertex.h"

namespace uml {

Transition::Transition(const QString& umlType)
    : NamedElement(umlType) {}

void Transition::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    NamedElement::fromJson(json, errorList);
    Namespace::fromJson(json, errorList);
    RedefinableElement::fromJson(json, errorList);
    container_ = JsonSerializable::create<Region>(json.value("container"), errorList);

    effect_ = JsonSerializable::create<Behavior>(json.value("effect"), errorList);

    guard_ = JsonSerializable::create<Constraint>(json.value("guard"), errorList);

    kind_ = (TransitionKind)json.value("kind").toInt((int)kind_);
    redefinedTransition_ = JsonSerializable::create<Transition>(json.value("redefinedTransition"), errorList);

    redefinitionContext_ = JsonSerializable::create<Classifier>(json.value("redefinitionContext"), errorList);

    source_ = JsonSerializable::create<Vertex>(json.value("source"), errorList);

    target_ = JsonSerializable::create<Vertex>(json.value("target"), errorList);

    JsonSerializable::fromJsonArray(triggers_, "triggers", json, errorList);
}

void Transition::toJson(QJsonObject& json) {
    NamedElement::toJson(json);
    Namespace::toJson(json);
    RedefinableElement::toJson(json);
    if (container_) {
        QJsonObject container_json;
        container_->toJson(container_json);
        json.insert("container", container_json);
    }
    if (effect_) {
        QJsonObject effect_json;
        effect_->toJson(effect_json);
        json.insert("effect", effect_json);
    }
    if (guard_) {
        QJsonObject guard_json;
        guard_->toJson(guard_json);
        json.insert("guard", guard_json);
    }
    json.insert("kind", (int)kind_);
    if (redefinedTransition_) {
        QJsonObject redefinedTransition_json;
        redefinedTransition_->toJson(redefinedTransition_json);
        json.insert("redefinedTransition", redefinedTransition_json);
    }
    if (redefinitionContext_) {
        QJsonObject redefinitionContext_json;
        redefinitionContext_->toJson(redefinitionContext_json);
        json.insert("redefinitionContext", redefinitionContext_json);
    }
    if (source_) {
        QJsonObject source_json;
        source_->toJson(source_json);
        json.insert("source", source_json);
    }
    if (target_) {
        QJsonObject target_json;
        target_->toJson(target_json);
        json.insert("target", target_json);
    }
    JsonSerializable::toJsonArray(triggers_, "triggers", json);
}

/// <summary>
/// containingStateMachine
    /// The query containingStateMachine() returns the StateMachine that contains the
    /// Transition either directly or transitively.
/// </summary>
    /// <code>
    /// result = (container.containingStateMachine())
    /// </code>
shared_ptr<StateMachine> Transition::containingStateMachine() const
{
    return nullptr;
}

/// <summary>
/// isConsistentWith
    /// The query isConsistentWith() specifies that a redefining Transition is
    /// consistent with a redefined Transition provided that the redefining Transition
    /// has the following relation to the redefined Transition: A redefining Transition
    /// redefines all properties of the corresponding redefined Transition except the
    /// source State and the Trigger.
/// </summary>
    /// <code>
    /// result = (-- the following is merely a default body; it is expected that the specific form of this constraint will be specified by profiles
    /// true)
    /// </code>
    /// <code>
    /// redefiningElement.isRedefinitionContextValid(self)
    /// </code>
bool Transition::isConsistentWith(shared_ptr<RedefinableElement> redefiningElement) const
{
    return false;
}

/// <summary>
/// container getter
/// Designates the Region that owns this Transition.
/// </summary>
shared_ptr<Region> Transition::container() const {
    return container_;
}

/// <summary>
/// container setter
/// Designates the Region that owns this Transition.
/// </summary>
void Transition::setContainer(shared_ptr<Region> value) {
    container_ = value;
}

/// <summary>
/// effect getter
/// Specifies an optional behavior to be performed when the Transition fires.
/// </summary>
shared_ptr<Behavior> Transition::effect() const {
    return effect_;
}

/// <summary>
/// effect setter
/// Specifies an optional behavior to be performed when the Transition fires.
/// </summary>
void Transition::setEffect(shared_ptr<Behavior> value) {
    effect_ = value;
}

/// <summary>
/// guard getter
/// A guard is a Constraint that provides a fine-grained control over the firing of
/// the Transition. The guard is evaluated when an Event occurrence is dispatched by
/// the StateMachine. If the guard is true at that time, the Transition may be
/// enabled, otherwise, it is disabled. Guards should be pure expressions without
/// side effects. Guard expressions with side effects are ill formed.
/// </summary>
shared_ptr<Constraint> Transition::guard() const {
    return guard_;
}

/// <summary>
/// guard setter
/// A guard is a Constraint that provides a fine-grained control over the firing of
/// the Transition. The guard is evaluated when an Event occurrence is dispatched by
/// the StateMachine. If the guard is true at that time, the Transition may be
/// enabled, otherwise, it is disabled. Guards should be pure expressions without
/// side effects. Guard expressions with side effects are ill formed.
/// </summary>
void Transition::setGuard(shared_ptr<Constraint> value) {
    guard_ = value;
}

/// <summary>
/// kind getter
/// Indicates the precise type of the Transition.
/// </summary>
TransitionKind Transition::kind() const {
    return kind_;
}

/// <summary>
/// kind setter
/// Indicates the precise type of the Transition.
/// </summary>
void Transition::setKind(TransitionKind value) {
    kind_ = value;
}

/// <summary>
/// redefinedTransition getter
/// The Transition that is redefined by this Transition.
/// </summary>
shared_ptr<Transition> Transition::redefinedTransition() const {
    return redefinedTransition_;
}

/// <summary>
/// redefinedTransition setter
/// The Transition that is redefined by this Transition.
/// </summary>
void Transition::setRedefinedTransition(shared_ptr<Transition> value) {
    redefinedTransition_ = value;
}

/// <summary>
/// redefinitionContext getter
/// References the Classifier in which context this element may be redefined.
/// </summary>
shared_ptr<Classifier> Transition::redefinitionContext() const {
    return redefinitionContext_;
}

/// <summary>
/// redefinitionContext setter
/// References the Classifier in which context this element may be redefined.
/// </summary>
void Transition::setRedefinitionContext(shared_ptr<Classifier> value) {
    redefinitionContext_ = value;
}

/// <summary>
/// source getter
/// Designates the originating Vertex (State or Pseudostate) of the Transition.
/// </summary>
shared_ptr<Vertex> Transition::source() const {
    return source_;
}

/// <summary>
/// source setter
/// Designates the originating Vertex (State or Pseudostate) of the Transition.
/// </summary>
void Transition::setSource(shared_ptr<Vertex> value) {
    source_ = value;
}

/// <summary>
/// target getter
/// Designates the target Vertex that is reached when the Transition is taken.
/// </summary>
shared_ptr<Vertex> Transition::target() const {
    return target_;
}

/// <summary>
/// target setter
/// Designates the target Vertex that is reached when the Transition is taken.
/// </summary>
void Transition::setTarget(shared_ptr<Vertex> value) {
    target_ = value;
}

/// <summary>
/// triggers getter
/// Specifies the Triggers that may fire the transition.
/// </summary>
vector<shared_ptr<Trigger>>& Transition::triggers() {
    return triggers_;
}

} // namespace uml
